#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerInput.h"
#include "WGINavType.generated.h"




class UPanelWidget;
class UWidget;


DECLARE_DYNAMIC_DELEGATE_OneParam(FOnNavClickedEvent, UWidget*, Item);
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnNavSelectedEvent, UWidget*, Item);
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnNavUnSelectedEvent, UWidget*, Item);



enum EWGINavDirection
{
	Invalid,
	Up,
	Down,
	Right,
	Left
};

enum EWGINavPanelType
{
	SingleRow,
	FourDirections,
};


struct WIDGETGROUP_API FNavDirectionData
{
public:

	FNavDirectionData()
	{
		UpKeyName = NAME_None;
		DownKeyName = NAME_None;
		LeftKeyName = NAME_None;
		RightKeyName = NAME_None;
		Clear();
	}
	~FNavDirectionData() { Clear(); }

	EWGINavDirection FindNavDirection(const FInputActionKeyMapping& Mapping)
	{
		if (UpDirectionKeys)
			if (UpDirectionKeys->Contains(Mapping))
				return EWGINavDirection::Up;

		if (DownDirectionKeys)
			if (DownDirectionKeys->Contains(Mapping))
				return EWGINavDirection::Down;

		if (RightDirectionKeys)
			if (RightDirectionKeys->Contains(Mapping))
				return EWGINavDirection::Right;

		if (LeftDirectionKeys)
			if (LeftDirectionKeys->Contains(Mapping))
				return EWGINavDirection::Left;


		return EWGINavDirection::Invalid;
	}

	void Clear()
	{
		UpDirectionKeys = nullptr;
		DownDirectionKeys = nullptr;
		LeftDirectionKeys = nullptr;
		RightDirectionKeys = nullptr;
	}

	TArray<FInputActionKeyMapping>* UpDirectionKeys;
	FName UpKeyName;
	TArray<FInputActionKeyMapping>* DownDirectionKeys;
	FName DownKeyName;
	TArray<FInputActionKeyMapping>* LeftDirectionKeys;
	FName LeftKeyName;
	TArray<FInputActionKeyMapping>* RightDirectionKeys;
	FName RightKeyName;
};

USTRUCT()
struct WIDGETGROUP_API FNavDelegate
{
	GENERATED_BODY()
public:
	FNavDelegate()
		:Delegate_ClickedEvent(), Delegate_SelectedEvent(), Delegate_UnSelectedEvent()
	{

	}

	UPROPERTY()
		FOnNavClickedEvent Delegate_ClickedEvent;
	UPROPERTY()
		FOnNavSelectedEvent Delegate_SelectedEvent;
	UPROPERTY()
		FOnNavUnSelectedEvent Delegate_UnSelectedEvent;
};



class WIDGETGROUP_API WGINavPanelData
{

public:

	WGINavPanelData(UPanelWidget* inPanel, EWGINavPanelType inPanelType, bool inbLoop, int inSelectedIndex)
	{
		PanelType = inPanelType;
		Panel = inPanel;
		bLoop = inbLoop;
		SelectedIndex = inSelectedIndex;
		NavDirectionData = new FNavDirectionData();
		NavDelegate = nullptr;
		Column_FourDirections = 0;
		bActive = true;
	}

	~WGINavPanelData()
	{
		EnsureClear();
	}


	template< class T >
	T* GetNavPanel() { return CastChecked<T>(Panel); }
	UPanelWidget* GetNavPanel() { return Panel; }
	FNavDirectionData* GetNavDirectionData() { return NavDirectionData; }
	EWGINavPanelType GetNavPanelType() { return PanelType; }

	int GetSelectedIndex() { return SelectedIndex; }
	void SetSelectedIndex(const int inIndex) { SelectedIndex = inIndex; }

	bool GetIsLoop() { return bLoop; }
	void SetIsLoop(const bool bIsLoop) { bLoop = bIsLoop; }



	FNavDelegate* GetNavDelegate() { return NavDelegate; }
	bool HasNavDelegate() { return NavDelegate ? true : false; }
	void EnsureClearNavDelegate()
	{
		if (NavDelegate)
		{
			delete NavDelegate;
			NavDelegate = nullptr;
		}
	}
	void EnsureNavDelegateValid()
	{
		if (!NavDelegate)
		{
			NavDelegate = new FNavDelegate();
		}
	}


	void SetColumn(int inColumn) { Column_FourDirections = inColumn; }
	int GetColumn() { return Column_FourDirections; }


	bool bActive;

private:
	void EnsureClear()
	{
		delete NavDirectionData;
		NavDirectionData = nullptr;

		EnsureClearNavDelegate();
	}

	UPanelWidget* Panel;
	EWGINavPanelType PanelType;
	int SelectedIndex;
	bool bLoop;
	FNavDirectionData* NavDirectionData;
	FNavDelegate* NavDelegate;

	int Column_FourDirections;

public:
	bool operator==(const WGINavPanelData*& Data) const;
	bool operator==(UPanelWidget*& inPanel) const;
	bool operator!=(const WGINavPanelData*& Data) const;

};

FORCEINLINE bool WGINavPanelData::operator==(const WGINavPanelData*& Data) const
{
	return this->Panel == Data->Panel;
}
FORCEINLINE bool WGINavPanelData::operator==(UPanelWidget*& inPanel) const
{
	return this->Panel == inPanel;
}
FORCEINLINE bool WGINavPanelData::operator!=(const WGINavPanelData*& Data) const
{
	return this->Panel != Data->Panel;
}

